package models;

import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;
import models.annotations.Shard;
import org.apache.commons.lang.StringUtils;
import org.h2.util.JdbcUtils;
import play.Logger;
import play.db.DB;
import play.db.jpa.GenericModel;
import play.db.jpa.JPABase;
import utils.ShortUUID;
import utils.db.ContextForUpdate;
import utils.db.ModelHelper;

import javax.persistence.*;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@MappedSuperclass
public class DateModel extends GenericModel {

    @Id
    public String id;

    @Column(name = "create_time")
    public Date createTime;

    @Column(name = "update_time")
    public Date updateTime;

    @Transient
    public boolean isModelNew = true;

    void beforeSave() {
        Date now = new Date();
        if (createTime == null) {
            createTime = now;
        }
        if (StringUtils.isEmpty(id)) {
            id = generateId();
            isModelNew = true;
        }else{
            isModelNew = false;
        }
        updateTime = now;
    }

    @Override
    public <T extends JPABase> T save() {
        beforeSave();
        if(checkShard() && !isModelNew){
            isModelNew = false;
            return updateWithShardColumn();
        }else{
            isModelNew = false;
            return super.save();
        }
    }

    private boolean checkShard(){
        for(Field field : this.getClass().getDeclaredFields()){
            if(field.isAnnotationPresent(Shard.class)){
                return true;
            }
        }
        return false;
    }

    private <T extends JPABase> T updateWithShardColumn(){
        ContextForUpdate context = ModelHelper.buildUpdateSql(this.getClass(), this);
        Monitor monitor = MonitorFactory.start("SQL:" + context.sql);

        Connection con = null;
        PreparedStatement stmt = null;

        monitor.start();
        try{
            PersistenceUnit pu = this.getClass().getAnnotation(PersistenceUnit.class);
            if(pu == null){
                con = DB.getConnection();
            }else{
                con = DB.getConnection(pu.name());
            }
            Logger.info("SQL:%s", context.sql);
            stmt = con.prepareStatement(context.sql);
            List<Object> params = new ArrayList();
            params.addAll(context.dataParams);
            params.addAll(context.keyParams);
            ModelHelper.setPreparedStatementParams(stmt, params.toArray());
            stmt.executeUpdate();
        }catch (Exception e){
            Logger.error(e, "Error in updateWithShardColumn");
        }finally {
            JdbcUtils.closeSilently(stmt);
        }
        monitor.stop();
        return (T) this;
    }

    public void setId(String id){
        this.id = id;
    }

    public String getId() {
        return id;
    }

    @Override
    public Object _key() {
        return getId();
    }

    protected String generateId() {
        return ShortUUID.generate();
    }

    public static enum BoolEnum {

        FALSE(0, "否"),
        TRUE(1, "是"),
        NO(0, "否"),
        YES(1, "是"),;

        public int value;
        public String description;

        private BoolEnum(int val, String desc) {
            this.value = val;
            this.description = desc;
        }

        public static BoolEnum get(Integer value) {
            for (BoolEnum v : values()) {
                if (value == v.value) {
                    return v;
                }
            }
            return null;
        }

        @Override
        public String toString() {
            return description;
        }
    }
}
